const express = require('express')
const blogTable = require('../../db/blog/blogTable')
const userTable = require('../../db/user/userTable')
const userDetailTable = require('../../db/user/userDetailTable')
const tipOffCommentTable = require('../../db/tip-off/tipOffCommentTable')
const { v4 } = require('uuid')
const axios = require('axios')

// 博客服务
const blogApp = express()

// 创建博客
blogApp.post('/createBlog', async (req, res) => {

  const user = await userTable.findOne({ token: req.headers['authorization'] })
  if (!user) {
    return res.send({
      status: 401,
      message: '无操作权限，请先登录！'
    })
  }

  const { title, tags, description, cover, content } = req.body

  // 新博客数据初始化
  let newBlog = {
    title,
    tags,
    description,
    cover,
    content,
    comment: [],
    author_id: user._id,
    lastModified: new Date(),
    views: 0,
    likes: 0,
    blogId: 1,
    approved: true
  }

  // 手动生成新博客的自增 id 保证博客 id 唯一性
  const blog = await blogTable.findOne({}, { blogId: true }, { sort: { blogId: -1 } })
  if (blog) {
    newBlog.blogId += blog.blogId
  }

  try {
    // 博客数据表中新增博客数据
    await blogTable.create(newBlog)
    // 用户详情表中新增文章id
    await userDetailTable.updateOne(
      { key: user.key },
      { $push: { articles: newBlog.blogId } }
    )
    res.send({
      status: 200,
      message: '发布成功！'
    })
  } catch (e) {
    console.log(e)
    res.send({
      status: 500,
      message: '发布失败！'
    })
  }

})

// 记录访客 ip 地址
let userIp = ''
// 高德地图的 key
const gaoDeKey = '7b7840935c27cf0b714db24c967d9e4d'
// 获取所有已审核的文章 用于广场展示
blogApp.get('/publicBlogs', async (req, res) => {
  let ip = req.ip
  // 获取访客 ip
  if (ip.substr(0, 7) === '::ffff:') {
    ip = ip.substr(7)
    if (ip !== userIp) {
      userIp = ip
      // 获取 IP 所在地
      const ipRes = await axios.get('https://restapi.amap.com/v3/ip', {
        params: {
          key: gaoDeKey,
          ip,
          output: 'json'
        }
      })

      if (ipRes.data.status === '1') {

        let location = []
        // 拿到 ip 经纬度坐标
        const ipAddr = ipRes.data.rectangle

        if (typeof ipAddr === 'string') {
          const ipLoctionArr = ipAddr.split(';')
          // 左下角坐标
          const lb = ipLoctionArr[0].split(',').map(item => Number(item))
          // 右上角坐标
          const rt = ipLoctionArr[1].split(',').map(item => Number(item))
          // 计算中心点坐标
          const center = [
            (lb[0] + rt[0]) / 2,
            (lb[1] + rt[1]) / 2
          ]
          location.push(...center)

          // 更新 访客 ip 信息
          const ipData = {
            ip: userIp,
            visitTime: new Date(),
            location,
            provience: ipRes.data.province.length ? ipRes.data.province : '',
            city: ipRes.data.city.length ? ipRes.data.city : ''
          }

          try {
            await visitorIpAddrTable.create(ipData)
          } catch (e) {
            console.log(e)
          }
        }
      }
    }
  }

  let params = {
    approved: true
  }
  let { limit, offset, searchKey } = req.query
  // 根据关键词模糊查询文章标题
  if (searchKey) {
    params['$or'] = [
      { title: { $regex: searchKey } },
      { tags: { $regex: searchKey } },
      { description: { $regex: searchKey } }
    ]
  }
  // 获取用户黑名单
  let blackList = []
  if (req.headers.authorization) {
    // 只有登录的用户有黑名单
    const userRes = await userTable.findOne({ token: req.headers.authorization })
    let userDetails = []
    let blackers = []
    if (userRes) {
      userDetails = await userDetailTable.findOne({ key: userRes.key })
      blackers = await userDetailTable.find({ blackList: { $in: [String(userRes._id)] } })
    }

    // 我的黑名单
    blackList = userDetails?.blackList || []

    // 接着 获取把我拉黑的人的名单
    let blackersKey = []
    blackers.forEach(item => blackersKey.push(item.key))
    const blackerInfos = await userTable.find({ key: { $in: blackersKey } })
    blackerInfos.forEach(item => blackList.push(item._id))
  }

  // 博客总数
  let blogTotal = 0
  // 当用户有黑名单的时候 加个查询条件
  if (blackList.length) {
    // 看不到被拉黑人的动态
    params['author_id'] = { $nin: blackList }
  }

  // 先查询总数量
  let totalRes = await blogTable.find({ ...params })
  blogTotal = totalRes.length

  // 然后再做分页处理 按照时间顺序倒序排列
  let blogRes = await blogTable.find(
    { ...params },
    {
      content: false,
      _id: false,
      __v: false
    },
    {
      limit: Number(limit),
      skip: Number(offset),
      sort: { lastModified: -1 }
    }
  )

  // 更新作者的用户名和头像信息
  const authors = []
  blogRes.forEach(item => authors.push(item.author_id))
  const authorInfo = await userTable.find(
    { _id: { $in: authors } },
    { _id: true, userName: true, avatar: true }
  )

  // 拷贝一份博客数据 不然直接在其中添加属性似乎添加不了
  const blogData = JSON.parse(JSON.stringify(blogRes))
  blogData.forEach((item) => {
    let user = authorInfo.find(author => String(author._id) === String(item.author_id))
    let author = {
      userName: user?.userName ? user.userName : '该账号已注销',
      avatar: user?.avatar ? user.avatar : '',
    }
    item.author = author
  })

  res.send({
    status: 200,
    message: 'OK！',
    data: {
      blogList: blogData,
      blogTotal
    }
  })

})

// 根据文章 id  查询文章详情信息
blogApp.get('/blogDetails', async (req, res) => {
  let params = { blogId: Number(req.query.blogId) }

  // 查询文章是否存在
  let blog = await blogTable.findOne(params, { _id: false, __v: false })

  if (blog) {
    // 有用户请求一次 改文章浏览数就 +1 按人次计数 而非人数计数
    await blogTable.updateOne(params, { $inc: { views: 1 } })

    // 查到文章详情
    let blogData = await blogTable.findOne(params, { _id: false, __v: false })

    // 更新作者信息
    let author = await userTable.findOne(
      { _id: blogData.author_id },
      { _id: false, userName: true, avatar: true, isAdmin: true },
    )

    if (!author) {
      author = {
        userName: '该账号已注销',
        avatar: '',
        isAdmin: false
      }
    }

    blogData = JSON.parse(JSON.stringify(blogData))

    res.send({
      status: 200,
      message: '查询成功！',
      data: {
        blogData: { ...blogData, author }
      }
    })

  } else {
    res.send({
      status: 404,
      message: '文章不存在或已被删除！'
    })
  }
})

// 获取当前用户的所有文章
blogApp.get('/myBlogs', async (req, res) => {
  let params = {}
  // 分页参数
  let { limit, offset } = req.query

  // 没登录无权限操作
  if (!req.headers.authorization) {
    return res.send({ status: 401, message: '无操作权限！' })
  }

  // 找到当前用户
  const userRes = await userTable.findOne({ token: req.headers.authorization })
  params['author_id'] = userRes._id

  // 博客总数
  let blogTotal = 0

  // 先查询总数量
  let totalRes = await blogTable.find({ ...params })
  blogTotal = totalRes.length

  // 然后再做分页处理 按照时间顺序倒序排列
  let blogRes = await blogTable.find(
    { ...params },
    {
      content: false,
      _id: false,
      __v: false
    },
    {
      limit: Number(limit),
      skip: Number(offset),
      sort: { lastModified: -1 }
    }
  )

  // 更新用户名和头像信息
  const blogData = JSON.parse(JSON.stringify(blogRes))
  blogData.forEach((item, index) => {
    item.author = {
      userName: userRes.userName,
      avatar: userRes.avatar
    }
  })

  res.send({
    status: 200,
    message: 'OK！',
    data: {
      blogList: blogData,
      blogTotal
    }
  })
})

// 好友动态的所有已过审的文章（包括自己的）
blogApp.get('/attentionArts', async (req, res) => {
  // 分页参数
  let { limit, offset } = req.query

  // 没登录无权限操作
  if (!req.headers.authorization) {
    return res.send({ status: 401, message: '无操作权限！' })
  }

  // 找到当前用户
  const userRes = await userTable.findOne({ token: req.headers.authorization })
  // 查询当前用户的的关注列表
  const attentions = (await userDetailTable.findOne({ key: userRes.key })).attentions
  // 查询所有好友的信息
  const users = await userTable.find({ _id: { $in: attentions } })
  // 自己也丢进去
  users.push(userRes)
  const keys = []
  users.forEach(user => keys.push(user.key))
  // 获取所有好友的文章 ID
  const blogIds = []
  const userDetails = await userDetailTable.find({ key: { $in: keys } })
  userDetails.forEach(item => blogIds.push(...item.articles))

  // 博客总数
  let blogTotal = 0

  // 先查询总数量
  let totalRes = await blogTable.find({ blogId: { $in: blogIds }, approved: true })
  blogTotal = totalRes.length

  // 然后再做分页处理 按照时间顺序倒序排列
  let blogRes = await blogTable.find(
    { blogId: { $in: blogIds }, approved: true },
    {
      content: false,
      _id: false,
      __v: false
    },
    {
      limit: Number(limit),
      skip: Number(offset),
      sort: { lastModified: -1 }
    }
  )

  // 更新作者的用户名和头像信息
  // 拷贝一份博客数据 不然直接在其中添加属性似乎添加不了
  const blogData = JSON.parse(JSON.stringify(blogRes))
  blogData.forEach((item) => {
    const user = users.find((user) => String(user._id) === String(item.author_id))
    let userInfo = {
      userName: user?.userName ? user.userName : '该账号已注销',
      avatar: user?.avatar ? user.avatar : ''
    }
    item.author = userInfo
  })

  res.send({
    status: 200,
    message: 'OK！',
    data: {
      blogList: blogData,
      blogTotal
    }
  })
})

// 获取当前用户点过赞的所有文章
blogApp.get('/likedBlogs', async (req, res) => {
  let params = {}
  // 分页参数
  let { limit, offset } = req.query

  // 没登录无权限操作
  if (!req.headers.authorization) {
    return res.send({ status: 401, message: '无操作权限！' })
  }

  // 找到当前用户
  const userRes = await userTable.findOne({ token: req.headers.authorization })

  // 查询当前用户点过赞的所有文章 id 
  const userDetails = await userDetailTable.findOne({ key: userRes.key })
  params['blogId'] = { $in: userDetails.likes }

  // 博客总数
  let blogTotal = 0

  // 先查询总数量
  let totalRes = await blogTable.find({ ...params })
  blogTotal = totalRes.length

  // 然后再做分页处理 按照时间顺序倒序排列
  let blogRes = await blogTable.find(
    { ...params },
    {
      content: false,
      _id: false,
      __v: false
    },
    {
      limit: Number(limit),
      skip: Number(offset),
      sort: { lastModified: -1 }
    }
  )

  // 更新作者的用户名和头像信息
  const authors = []
  blogRes.forEach(item => authors.push(item.author_id))
  const authorInfo = await userTable.find(
    { _id: { $in: authors } },
    { _id: true, userName: true, avatar: true }
  )
  // 拷贝一份博客数据 不然直接在其中添加属性似乎添加不了
  const blogData = JSON.parse(JSON.stringify(blogRes))
  blogData.forEach((item) => {
    let user = authorInfo.find(author => String(author._id) === String(item.author_id))
    let author = {
      userName: user?.userName ? user.userName : '该账号已注销',
      avatar: user?.avatar ? user.avatar : ''
    }
    item.author = author
  })

  res.send({
    status: 200,
    message: 'OK！',
    data: {
      blogList: blogData,
      blogTotal
    }
  })

})

// 获取当前用户的评论合辑
blogApp.get('/commentedBlogs', async (req, res) => {
  let params = {}
  // 分页参数
  let { limit, offset } = req.query

  // 没登录无权限操作
  if (!req.headers.authorization) {
    return res.send({ status: 401, message: '无操作权限！' })
  }

  // 找到当前用户
  const userRes = await userTable.findOne({ token: req.headers.authorization })

  // 查询当前用户评论过的所有文章 id 
  const userDetails = await userDetailTable.findOne({ key: userRes.key })

  params['blogId'] = { $in: userDetails.comments.map(item => item.blogId) }
  // 博客总数
  let blogTotal = 0

  // 先查询总数量
  let totalRes = await blogTable.find({ ...params })
  blogTotal = totalRes.length

  // 然后再做分页处理 按照时间顺序倒序排列
  let blogRes = await blogTable.find(
    { ...params },
    {
      content: false,
      _id: false,
      __v: false
    },
    {
      limit: Number(limit),
      skip: Number(offset),
      sort: { lastModified: -1 }
    })

  res.send({
    status: 200,
    message: 'OK！',
    data: {
      blogList: blogRes,
      blogTotal
    }
  })
})

// 当前登录用户
let currentUser = null
// 后续操作统一权限认证
const permissionRoutes = ['/comment', '/comment/*', '/comments', '/comments/*', '/delBlog', '/delBlog/*']

blogApp.all(permissionRoutes, async (req, res, next) => {
  let permission = await userTable.findOne({ token: req.headers.authorization })
  if (permission) {
    currentUser = permission
    next()
  } else {
    return res.send({
      status: 403,
      message: '无操作权限'
    })
  }
})

// 接收一级评论
blogApp.post('/comment', async (req, res) => {

  const { blogId, content } = req.body

  const commentData = {
    author_id: currentUser._id,
    content,
    likes: [],
    commentId: v4(),
    lastModified: new Date(),
    replies: [],
  }

  // 博客详情表中 保存新评论
  const saveRes = await blogTable.updateOne(
    { blogId },
    {
      $push: {
        comment: {
          $each: [commentData],
          $position: 0
        }
      }
    }
  )

  if (saveRes.modifiedCount > 0) {

    // 用户详情表中添加新的评论信息
    const rs = await userDetailTable.updateOne(
      {
        key: currentUser.key,
        comments: { $elemMatch: { blogId } }
      },
      {
        $push: {
          'comments.$.commentData': {
            $each: [{
              content,
              commentId: commentData.commentId,
              lastModified: commentData.lastModified
            }],
            $position: 0
          }
        }
      }
    )

    // 说明现在还没有评论数据 就新增一个
    if (rs.modifiedCount < 1) {
      await userDetailTable.updateOne(
        { key: currentUser.key },
        {
          $push: {
            comments: {
              blogId,
              commentData: [{
                content,
                commentId: commentData.commentId,
                lastModified: commentData.lastModified
              }]
            }
          }
        }
      )
    }

    res.send({
      status: 200,
      message: '评论成功！'
    })
  } else {
    res.send({
      status: 500,
      message: '评论失败！'
    })
  }
})

// 删除一级评论
blogApp.delete('/comment/:blogId/:author_id/:commentId', async (req, res) => {
  const { blogId, author_id, commentId } = req.params

  const deleteRes = await blogTable.updateOne(
    { blogId },
    { $pull: { comment: { commentId } } }
  )

  if (deleteRes.modifiedCount > 0) {

    // 删除对该评论的所有举报信息
    await tipOffCommentTable.deleteMany({ commentId })

    // 默认查询参数 就是评论者本人
    let query = {
      key: currentUser.key
    }
    // 如果删除的人不是写评论的人 那就说明删除人是管理员或者文章作者
    if (currentUser._id !== author_id) {
      const rs = await userTable.findOne({ _id: author_id })
      query.key = rs.key
    }

    // 用户详情表中移除对应的评论信息
    const delRes = await userDetailTable.updateOne(
      {
        ...query,
        comments: { $elemMatch: { blogId: Number(blogId) } }
      },
      {
        $pull: {
          'comments.$.commentData': {
            commentId
          }
        }
      }
    )

    if (delRes.modifiedCount > 0) {

      // 如果对该文章的评论都清空了 就把对该文章的整个文档记录删掉
      await userDetailTable.updateOne(
        { ...query },
        { $pull: { comments: { commentData: { $exists: true, $size: 0 } } } }
      )

      res.send({
        status: 200,
        message: '删除成功！'
      })
    }
  } else {
    res.send({
      status: 500,
      message: '删除失败！'
    })
  }
})

// 接收二级评论（回复）
blogApp.post('/comment/reply', async (req, res) => {

  const { blogId, content, replyTo, parentId } = req.body

  const replyData = {
    author_id: currentUser._id,
    commentId: v4(),
    replyTo,
    likes: [],
    content,
    lastModified: new Date()
  }

  let replyRes = {
    status: 500,
    message: '回复失败！'
  }

  let updateRes = null

  // 如果没有父级评论 ID 则认为该回复是对一级评论的回复 该回复的父级就是该一级评论
  // 采用头部插入的方式
  // 更新步骤： 找到文章 ——> 找到一级评论 ——> 在一级评论回复列表中加一条回复
  if (!parentId) {
    // 根据文章 id 找到该文章 更新评论
    updateRes = await blogTable.updateOne(
      {
        blogId,
        comment: { $elemMatch: { commentId: replyTo.commentId } }
      },
      { $push: { 'comment.$.replies': { $each: [replyData], $position: 0 } } }
    )

  } else {
    // 有父级 id 说明是对子评论的回复
    // 更新步骤： 找到文章 ——> 找到一级评论 ——> 找到回复列表中待回复对象的位置 ——> 在它后面更新回复

    const reply = await blogTable.findOne({ blogId })

    let replyIndex = -1
    // 找到待回复子评论的索引
    reply.comment.forEach((item) => {

      if (replyIndex !== -1) return

      item.replies.forEach((reply, index) => {
        if (reply.commentId === replyTo.commentId) {
          replyIndex = index
          return
        }
      })
    })

    // 在其后面新增一条回复
    updateRes = await blogTable.updateOne(
      {
        blogId,
        comment: { $elemMatch: { commentId: parentId } }
      },
      { $push: { 'comment.$.replies': { $each: [replyData], $position: replyIndex + 1 } } }
    )
  }

  if (updateRes.modifiedCount > 0) {
    replyRes.status = 200
    replyRes.message = '回复成功！'
  }

  res.send(replyRes)
})
// 删除二级评论
blogApp.delete('/comment/reply/:blogId/:parentId/:commentId', async (req, res) => {
  // 需要拿到文章 id  一级评论 id 和 待删除的子评论 id
  const { blogId, parentId, commentId } = req.params

  const deleteRes = {
    status: 200,
    message: '删除成功！'
  }

  // 查找到子评论 并做删除
  const rs = await blogTable.updateOne(
    {
      blogId: Number(blogId),
      comment: { $elemMatch: { commentId: parentId } }
    },
    { $pull: { 'comment.$.replies': { commentId } } }
  )

  if (rs.modifiedCount < 1) {
    deleteRes.status = 500
    deleteRes.message = '删除失败！'
  }

  // 删除对该评论的所有举报信息
  await tipOffCommentTable.deleteMany({ commentId })

  res.send(deleteRes)
})

// 获取全部评论
blogApp.get('/comments', async (req, res) => {
  const { blogId } = req.query
  const rs = await blogTable.findOne({ blogId: Number(blogId) })

  let findRes = {
    status: 200,
    message: '查询成功！'
  }

  if (!rs) {
    findRes.status = 404
    findRes.message = '查询失败！'
  } else {
    findRes.data = {}
    // 更新所有评论者的名字和头像 用 Set 去重
    let authors = new Set()
    // 
    rs.comment.forEach(item => {
      authors.add(String(item.author_id))
      if (item.replies.length) {
        item.replies.forEach(reply => {
          authors.add(String(reply.author_id))
          authors.add(String(reply.replyTo.author_id))
        })
      }
    })
    // 转为数组
    authors = [...authors]
    // 匹配到所有的评论者的信息
    let authorsInfo = await userTable.find(
      { _id: { $in: authors } },
      { _id: true, userName: true, avatar: true, isAdmin: true }
    )
    // 复制评论数据
    findRes.data.comments = JSON.parse(JSON.stringify(rs.comment))

    // 对数据进行加工
    findRes.data.comments.forEach(item => {
      // 对一级评论加工
      let author = authorsInfo.find(user => String(item.author_id) === String(user._id))
      if (author) {
        item.userData = {
          userId: author._id,
          name: author.userName,
          avatar: author.avatar,
          isAdmin: author.isAdmin
        }
      } else {
        item.userData = {
          userId: item.author_id,
          name: '该账号已注销',
          avatar: '',
          isAdmin: false
        }
      }

      // 对二级评论加工
      if (item.replies.length) {
        item.replies.forEach(reply => {
          let author = authorsInfo.find(user => String(reply.author_id) === String(user._id))
          if (author) {
            reply.userData = {
              userId: author._id,
              name: author.userName,
              avatar: author.avatar,
              isAdmin: author.isAdmin
            }
          } else {
            reply.userData = {
              userId: item.author_id,
              name: '该账号已注销',
              avatar: '',
              isAdmin: false
            }
          }

          let replyTo = authorsInfo.find(user => String(reply.replyTo.author_id) === String(user._id))

          if (replyTo) {
            reply.replyTo.name = replyTo.userName
            reply.replyTo.isAdmin = replyTo.isAdmin
          } else {
            reply.replyTo.name = '该账号已注销'
            reply.replyTo.isAdmin = false
          }

        })
      }
    })

  }

  res.send(findRes)
})

// 对评论点赞
blogApp.post('/comment/like', async (req, res) => {
  const { blogId, parentId, commentId } = req.body

  let likeRes = {
    status: 200,
    message: '点赞成功！'
  }
  let rs = null

  // 对一级评论的点赞
  if (!parentId) {
    rs = await blogTable.updateOne(
      {
        blogId,
        comment: { $elemMatch: { commentId } }
      },
      { $push: { 'comment.$.likes': currentUser._id } }
    )
  } else { // 对二级评论的点赞
    rs = await blogTable.findOneAndUpdate(
      {
        blogId,
        comment: { $elemMatch: { commentId: parentId } },
      },
      { $push: { 'comment.$.replies.$[c].likes': currentUser._id } },
      // 嵌套关系比较复杂，使用数组过滤器来实现
      { arrayFilters: [{ 'c.commentId': commentId }], new: true }
    )
  }

  if (rs.modifiedCount < 1 || !rs) {
    likeRes.status = 500
    likeRes.message = '点赞失败！'
  }

  res.send(likeRes)
})

// 取消评论点赞
blogApp.delete('/comment/like', async (req, res) => {
  const { blogId, parentId, commentId } = req.body

  let likeRes = {
    status: 200,
    message: '取消成功！'
  }
  let rs = null

  // 取消对一级评论的点赞
  if (!parentId) {
    rs = await blogTable.updateOne(
      {
        blogId,
        comment: { $elemMatch: { commentId } }
      },
      { $pull: { 'comment.$.likes': currentUser._id } }
    )
  } else { // 取消对二级评论的点赞
    rs = await blogTable.findOneAndUpdate(
      {
        blogId,
        comment: { $elemMatch: { commentId: parentId } }
      },
      { $pull: { 'comment.$.replies.$[c].likes': currentUser._id } },
      { arrayFilters: [{ 'c.commentId': commentId }], new: true }
    )
  }

  if (rs.modifiedCount < 1 || !rs) {
    likeRes.status = 500,
      likeRes.message = '取消失败！'
  }

  res.send(likeRes)
})

// 删除文章
blogApp.delete('/delBlog/:blogId', async (req, res) => {
  let { blogId } = req.params
  blogId = Number(blogId)

  // 找到文章作者
  const author_id = (await blogTable.findOne({ blogId })).author_id
  // 删除该文章
  const delRes = await blogTable.deleteOne({ blogId })

  if (delRes.deletedCount < 1) {
    return res.send({
      status: 500,
      message: '删除失败！'
    })
  }

  // 该作者文章列表里删掉该文章
  const author = await userTable.findOne({ _id: author_id })
  if (author) {
    await userDetailTable.updateOne(
      { key: author.key },
      { $pull: { articles: blogId } }
    )
  }

  // 删除所有人对该文章的点赞数据
  await userDetailTable.updateMany(
    { likes: blogId },
    { $pull: { likes: blogId } }
  )

  // 删除所有人对该文章的评论数据
  await userDetailTable.updateMany(
    { comments: { $elemMatch: { blogId } } },
    { $pull: { comments: { blogId } } }
  )

  res.send({
    status: 200,
    message: '删除成功！'
  })

})

module.exports = blogApp
